package com.zb.hello.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;

import org.docx4j.XmlUtils;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Tc;
import org.docx4j.wml.Text;
import org.docx4j.wml.Tr;

public class Docx4jUtil {

	public static Builder of(String path) throws FileNotFoundException, Docx4JException {
		return new Builder(path);
	}

	public static class Builder {
		private WordprocessingMLPackage template = null;
		private Iterator<Text> texts = null;

		// 占位符参数map
		private Map<String, String> params = new HashMap<String, String>();

		private Builder(String path) throws FileNotFoundException, Docx4JException {
			if (path != null && !path.isEmpty()) {
				this.template = WordprocessingMLPackage.load(new FileInputStream(new File(path)));
				this.texts = getAllPlaceholderElementFromObject(template.getMainDocumentPart()).iterator();
			}
		}

		/**
		 * 增加文本占位符参数(一个)
		 * 
		 * @param key
		 *            键
		 * @param value
		 *            值
		 * @return
		 */
		public Builder addParam(String key, String value) {
			Builder builder = this;
			if (key != null && !key.isEmpty()) {
				while (texts.hasNext()) {
					Text text = texts.next();
					String temp = text.getValue();
					if (temp.equals("${" + key + "}")) {
						text.setValue(value);
						texts.remove();
						return builder;
					}
				}
			}
			return builder;
		}

		/**
		 * 增加参数(多个)
		 * 
		 * @param params
		 *            多个参数的map
		 * @return
		 */
		public Builder addParams(Map<String, String> params) {
			this.params = params;
			return this;
		}

		/**
		 * 增加一个表格
		 * 
		 * @param tablePlaceholder
		 *            寻找表格的占位符
		 * @param placeholderRows
		 *            模板行所占行数
		 * @param list
		 *            替换模板占位符的数据
		 * @return
		 * @throws JAXBException
		 * @throws Docx4JException
		 */
		public Builder addTable(String tablePlaceholder, int placeholderRows, List<Map<String, String>> list)
				throws Docx4JException, JAXBException {
			List<Object> tables = getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);

			Tbl tempTable = getTemplateTable(tables, tablePlaceholder);
			List<Object> trs = getAllElementFromObject(tempTable, Tr.class);
			int rows = trs.size();

			if (rows > placeholderRows) {
				List<Tr> tempTrs = new ArrayList<>();
				for (int i = rows - placeholderRows; i < rows; i++) {
					tempTrs.add((Tr) trs.get(i));
				}

				for (Map<String, String> trData : list) {
					for (Tr tempTr : tempTrs) {
						addRowToTable(tempTable, tempTr, trData);
					}
				}
				
				for (Tr tempTr : tempTrs) {
					tempTable.getContent().remove(tempTr);
				}
			}
			
			return this;

		}
		
		public Builder addImg(String placeholder,byte[] decodeBuffer) throws Docx4JException, JAXBException {
			Inline inline = createInlineImage(template, decodeBuffer);
			P paragraph = addInlineImageToParagraph(inline);
			
			List<Object> tables = getAllElementFromObject(template.getMainDocumentPart(), Tbl.class);
			Tbl tempTable = getTemplateTable(tables, placeholder);
			List<Object> rows = getAllElementFromObject(tempTable, Tr.class);
			Tr tr = (Tr) rows.get(0);
			List<Object> cells = getAllElementFromObject(tr, Tc.class);
			Tc tc = (Tc) cells.get(0);
			tc.getContent().clear();
			tc.getContent().add(paragraph);
			return this;
		}

		/**
		 * 获取文件字节流
		 * 
		 * @return
		 * @throws FileNotFoundException
		 * @throws Docx4JException
		 */
		public byte[] get() throws FileNotFoundException, Docx4JException {
			if (!params.isEmpty()) {
				while (texts.hasNext()) {
					Text text = texts.next();
					String temp = text.getValue();
					for (Entry<String, String> param : params.entrySet()) {
						if (temp.equals("${" + param.getKey() + "}")) {
							text.setValue(param.getValue());
						}
					}
				}
			}

			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
			template.save(outputStream);
			return outputStream.toByteArray();
		}
	}

	/**
	 * 创建包含图片的一个内联对象
	 * @param wordMLPackage
	 * @param bytes
	 * @return
	 */
	public static Inline createInlineImage(WordprocessingMLPackage wordMLPackage,byte[] bytes){
		Inline inline = null;
		try {
			BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);
			int docPrId = 1;
			int cNvPrId = 2;
			inline = imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return inline;
	}
	
	/**
	 * 创建一个对象工厂并用它创建一个段落和一个可运行块R. 然后将可运行块添加到段落中. 接下来创建一个图画并将其添加到可运行块R中. 最后我们将内联
	 * 对象添加到图画中并返回段落对象.
	 * 
	 * @param inline
	 *            包含图片的内联对象.
	 * @return 包含图片的段落
	 */
	private static P addInlineImageToParagraph(Inline inline) {
		// 添加内联对象到一个段落中
		ObjectFactory factory = new ObjectFactory();
		P paragraph = factory.createP();
		R run = factory.createR();
		paragraph.getContent().add(run);
		Drawing drawing = factory.createDrawing();
		run.getContent().add(drawing);
		drawing.getAnchorOrInline().add(inline);
		return paragraph;
	}
	
	// 发现docx文档包含占位符的文本节点
	private static List<Text> getAllPlaceholderElementFromObject(Object obj) {
		List<Text> result = new ArrayList<Text>();
		Class<Text> toSearch = Text.class;
		Text textPlaceholder = null;
		if (obj instanceof JAXBElement) {
			obj = ((JAXBElement<?>) obj).getValue();
		}
		if (obj.getClass().equals(toSearch)) {
			textPlaceholder = (Text) obj;
			if (isPlaceholder(textPlaceholder.getValue())) {
				result.add((Text) obj);
			}
		} else if (obj instanceof ContentAccessor) {
			List<?> children = ((ContentAccessor) obj).getContent();
			for (Object child : children) {
				result.addAll(getAllPlaceholderElementFromObject(child));
			}
		}
		return result;
	}
	
	// 发现docx文档中的节点
	private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
		List<Object> result = new ArrayList<Object>();
		if (obj instanceof JAXBElement) {
			obj = ((JAXBElement<?>) obj).getValue();
		}
		if (obj.getClass().equals(toSearch)) {
			result.add(obj);
		} else if (obj instanceof ContentAccessor) {
			List<?> children = ((ContentAccessor) obj).getContent();
			for (Object child : children) {
				result.addAll(getAllElementFromObject(child, toSearch));
			}
		}
		return result;
	}

	// 这个方法只是查看表格是否含有我们的占位符，如果有则返回表格
	private static Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {
		for (Iterator<Object> iterator = tables.iterator(); iterator.hasNext();) {
			Object tbl = iterator.next();
			List<?> textElements = getAllElementFromObject(tbl, Text.class);
			for (Object text : textElements) {
				Text textElement = (Text) text;
				if (textElement.getValue() != null && textElement.getValue().equals("${"+templateKey+"}")) {
					return (Tbl) tbl;
			}
			}
		}
		return null;
	}

	/**
	 * 复制模板行
	 * 
	 * @param reviewtable
	 *            表格
	 * @param templateRow
	 *            模板行
	 * @param replacements
	 *            填充模板行的数据
	 */
	private static void addRowToTable(Tbl reviewtable, Tr templateRow, Map<String, String> replacements) {
		Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);
		List<?> textElements = getAllElementFromObject(workingRow, Text.class);
		for (Object object : textElements) {
			Text text = (Text) object;
			String keyStr = getPlaceholderStr(text.getValue());
			if (keyStr != null && !keyStr.isEmpty()) {
				String replacementValue = (String) replacements.get(keyStr);
				if (replacementValue != null) {text.setValue(replacementValue);};
			}
		}

		reviewtable.getContent().add(workingRow);
	}
	
	/**
	 * 判断字符串是否有${}占位符
	 * @param str
	 * @return
	 */
	private static boolean isPlaceholder(String str) {
		if(str != null && !str.isEmpty()) {
		Pattern pattern = Pattern.compile("([$]\\{\\w+\\})");
		Matcher m = pattern.matcher(str);
		if (m.find()) {
			return true;
		}}
		return false;
	}
	
	/**
	 * 得到占位符${}中的文本
	 * @param str
	 * @return 
	 */
	private static String getPlaceholderStr(String str) {
		if(str != null && !str.isEmpty()) {
		Pattern p = Pattern.compile("\\$\\{(.*?)\\}");
    	Matcher m = p.matcher(str);
    	 if(m.find()) {
    		 	return m.group(1);//m.group(0)包括这两个字符
        }
		}
    	 return null;
	}
	
}
